﻿/**
        @mainpage LOG550 Prototype 1
        @author Benoit Paquet\n Gabriel Kenderessy\n Aleksandre Clavet\n
        @file main.c
        @brief
                \n
                L'objectif de ce laboratoire est d'implémenter les requis suivants dans un microcontrôleur Atmel 32UC3AC512-U:\n
                Tout d'abord, il faut être capable d'échantillonner le potentiomètre\n
                et le capteur de lumière à 1000 échantillons par seconde.\n
                En appuyant sur le bouton 0, il est possible de passer à 2000 échantillons par seconde.\n
                Appuyer à répétition permet de passer d'un mode à l'autre.\n
 */
#include <avr32/io.h>
#include "intc.h"
#include "compiler.h"
#include "board.h"
#include "power_clocks_lib.h"
#include "gpio.h"
#include "tc.h"
#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include "usart.h"

#include "board.h"
#include "gpio.h"
#include "pm.h"
#include "adc.h"

/** @brief Canal du potentiomètre */
#define ADC_POTENTIOMETER_CHANNEL 1
/** @brief Canal du capteur de lumière */
#define ADC_LIGHT_CHANNEL 2
/** @brief Vitesse d'acquisition normale */
#define NORMAL_SPEED 187
/** @brief Vitesse d'acquisition accélérée */
#define OVERDRIVE 93
/** @brief Fréquence de clignotement des LEDs */
#define NORMAL_LED_FREQUENCY 200
/** @brief Programme en mode d'échantillonage */
#define SAMPLING 's'
/** @brief Programme en mode d'attente */
#define WAITING 'x'
/** @brief Échantillonnage complété */
#define READY 0
/** @brief En processus de conversion et d'envoi d'échantillon */
#define BUSY 1
/** @brief Bouton relâché */
#define RELEASED 0
/** @brief Bouton appuyé */
#define PUSHED 1

U16 LED_FREQUENCY = NORMAL_LED_FREQUENCY;	/** @brief La fréquence de clignotement du LED */
U16 ELAPSED_TICKS = 0;						/** @brief Nombre de cycles depuis la dernière opération de LED */

U8 INPUT_CHANNEL = ADC_LIGHT_CHANNEL;		/** @brief Canal où l'échantillonage est effectué */
U8 OUTPUT_CHANNEL = 0x01;					/** @brief Canal où l'échantillon sra envoyé */
U8 MODE = WAITING; 							/** @brief Flag qui permet de lire ou envoyer des données sur le port sériel */
U8 READ_SAMPLE = READY; 					/** @brief Flag qui permet de dire s'il faut lire un échantillon */
U8 PB0_PUSHED = RELEASED;					/** @brief Flag pour déterminer si quelqu'un pousse sur le bouton 0 */
U8 SAMPLE = -1; 							/** @brief Conserve la valeur d'un échantillon (Potentiomètre ou capteur de lumière) */

static const tc_interrupt_t TC_INTERRUPT =
{
	.etrgs = 0,
	.ldrbs = 0,
	.ldras = 0,
	.cpcs  = 1,
	.cpbs  = 0,
	.cpas  = 0,
	.lovrs = 0,
	.covfs = 0
};

__attribute__((__interrupt__))
/** @brief Interruption pour le Timer Counter\n
 * Détermine quand il faut lire un échantillon\n
 * Contrôle les LEDs et alterne les canaux pour la lecture d'échantillon
 * */
static void tc_irq(void)
{
	tc_read_sr(&AVR32_TC, 0);
	ELAPSED_TICKS++;

	if(MODE == SAMPLING) READ_SAMPLE = BUSY;
	if(ELAPSED_TICKS == LED_FREQUENCY) ELAPSED_TICKS = 0;

	/*********** START ALTERNATE CHANNELS ***********/
	if(INPUT_CHANNEL == ADC_LIGHT_CHANNEL)
	{
		INPUT_CHANNEL = ADC_POTENTIOMETER_CHANNEL;
	}
	else
	{
		INPUT_CHANNEL = ADC_LIGHT_CHANNEL;
	}

	OUTPUT_CHANNEL = INPUT_CHANNEL - 1;
	/************ END ALTERNATE CHANNELS ************/

	/*********** START LED MANAGEMENT ***********/
	if(ELAPSED_TICKS % LED_FREQUENCY == 0)
	{
		gpio_tgl_gpio_pin(LED0_GPIO);

		if(MODE == SAMPLING)
		{
			gpio_tgl_gpio_pin(LED1_GPIO);
		}
		else
		{
			gpio_clr_gpio_pin(LED1_GPIO);
			gpio_set_gpio_pin(LED1_GPIO);
		}
	}
	/*********** END LED MANAGEMENT ***********/
}

__attribute__((__interrupt__))
/** @brief Interruption pour le ADC\n
 * Converti les signaux analogiques à des signaux digital
 * */
static void adc_irq(void)
{
	if(READ_SAMPLE == BUSY) gpio_clr_gpio_pin(LED2_GPIO);
	SAMPLE = adc_get_value(&AVR32_ADC, INPUT_CHANNEL);
	(&AVR32_ADC)->lcdr;
}

__attribute__((__interrupt__))
/** @brief Interruption pour le USART\n
 * Traite les commandes reçues par l'ordinateur maître
 * */
static void usart_irq(void)
{
	// Réception des commandes de l'ordinateur maître
	if (AVR32_USART1.csr & AVR32_USART_CSR_RXRDY_MASK)
	{
		MODE = AVR32_USART1.rhr & AVR32_USART_RHR_RXCHR_MASK;
		gpio_clr_gpio_pin(LED0_GPIO);
		gpio_clr_gpio_pin(LED1_GPIO);
	}
}

/** @brief Initialise le Timer Counter
 * Donne la vitesse du cristal et enregistre les interruptions
 * */
static void tc_init(void) {
        // Configuration du peripherique TC
        static const tc_waveform_opt_t WAVEFORM_OPT =
        {
			.channel  = 0,                        		   // Channel selection.

			.bswtrg   = TC_EVT_EFFECT_NOOP,                // Software trigger effect on TIOB.
			.beevt    = TC_EVT_EFFECT_NOOP,                // External event effect on TIOB.
			.bcpc     = TC_EVT_EFFECT_NOOP,                // RC compare effect on TIOB.
			.bcpb     = TC_EVT_EFFECT_NOOP,                // RB compare effect on TIOB.

			.aswtrg   = TC_EVT_EFFECT_NOOP,                // Software trigger effect on TIOA.
			.aeevt    = TC_EVT_EFFECT_NOOP,                // External event effect on TIOA.
			.acpc     = TC_EVT_EFFECT_NOOP,                // RC compare effect on TIOA: toggle.
			.acpa     = TC_EVT_EFFECT_NOOP,                // RA compare effect on TIOA: toggle
			.wavsel   = TC_WAVEFORM_SEL_UP_MODE_RC_TRIGGER,// Waveform selection: Up mode with automatic trigger(reset) on RC compare.
			.enetrg   = FALSE,                             // External event trigger enable.
			.eevt     = 0,                                 // External event selection.
			.eevtedg  = TC_SEL_NO_EDGE,                    // External event edge selection.
			.cpcdis   = FALSE,                             // Counter disable when RC compare.
			.cpcstop  = FALSE,                             // Counter clock stopped with RC compare.

			.burst    = FALSE,                             // Burst signal selection.
			.clki     = FALSE,                             // Clock inversion.
			.tcclks   = TC_CLOCK_SOURCE_TC4                // Internal source clock 3, connected to fPBA / 8.
        };

        /* Au reset, le microcontroleur opere sur un crystal interne a 115200Hz. */
        /* Nous allons le configurer pour utiliser un crystal externe, FOSC0, a 12Mhz. */
        pcl_switch_to_osc(PCL_OSC0, FOSC0, OSC0_STARTUP);

        // Enregistrement de la nouvelle IRQ du TIMER au Interrupt Controller .
        INTC_register_interrupt(&tc_irq, AVR32_TC_IRQ0, AVR32_INTC_INT0);

        tc_init_waveform(&AVR32_TC, &WAVEFORM_OPT);     // Initialize the timer/counter waveform.

        // Placons le niveau RC a atteindre pour declencher de l'IRQ.
        // Attention, RC est un 16-bits, valeur max 65535

        // We want: (1/(FOSC0/32)) * RC = 0.100 s, donc RC = (FOSC0/32) / 10  to get an interrupt every 100 ms.
        tc_write_rc(&AVR32_TC, 0, NORMAL_SPEED); // Set RC value.

        tc_configure_interrupts(&AVR32_TC, 0, &TC_INTERRUPT);

        // Start the timer/counter.
        tc_start(&AVR32_TC, 0);                    // And start the timer/counter.

}

/*!
 * \brief Initialisation ADC : Potentiomètre et capteur de lumière
 * Initialise le composant ADC qui gère les valeurs réels (analogique) du potentiomètre et du capteur de lumière
*/
void adc_init(void)
{
	// GPIO pin/adc-function map.
	static const gpio_map_t ADC_GPIO_MAP =
	{
		{AVR32_ADC_AD_2_PIN, AVR32_ADC_AD_2_FUNCTION},
		{AVR32_ADC_AD_1_PIN, AVR32_ADC_AD_1_FUNCTION}
	};

	// Assign and enable GPIO pins to the ADC function.
	gpio_enable_module(ADC_GPIO_MAP, sizeof(ADC_GPIO_MAP) / sizeof(ADC_GPIO_MAP[0]));

	// configure ADC
	// Lower the ADC clock to match the ADC characteristics (because we configured
	// the CPU clock to 12MHz, and the ADC clock characteristics are usually lower;
	// cf. the ADC Characteristic section in the datasheet).
	AVR32_ADC.mr |= 0x1 << AVR32_ADC_MR_PRESCAL_OFFSET;
	adc_configure(&AVR32_ADC);

	adc_enable(&AVR32_ADC, ADC_LIGHT_CHANNEL);
	adc_enable(&AVR32_ADC, ADC_POTENTIOMETER_CHANNEL);

    // Enable ADC DATA READY interrupt.
    (&AVR32_ADC)->ier = AVR32_ADC_DRDY_MASK;
    INTC_register_interrupt(&adc_irq, AVR32_ADC_IRQ, AVR32_INTC_INT0);
}

/** @brief Initialise le USART
 * Permet de lire et d'envoyer des données sur le port sériel
 * */
void usart_init(void) {
	  static const gpio_map_t USART_GPIO_MAP =
	  {
		  {AVR32_USART1_RXD_0_0_PIN, AVR32_USART1_RXD_0_0_FUNCTION},
		  {AVR32_USART1_TXD_0_0_PIN, AVR32_USART1_TXD_0_0_FUNCTION}
	  };

	  // USART options.
	  static const usart_options_t USART_OPTIONS =
	  {
	    .baudrate     = 57600,
	    .charlength   = 8,
	    .paritytype   = USART_NO_PARITY,
	    .stopbits     = USART_1_STOPBIT,
	    .channelmode  = USART_NORMAL_CHMODE
	  };


	  // Configure Osc0 in crystal mode (i.e. use of an external crystal source, with
	  // frequency FOSC0) with an appropriate startup time then switch the main clock
	  // source to Osc0.
	  pcl_switch_to_osc(PCL_OSC0, FOSC0, OSC0_STARTUP);

	  // Assign GPIO to USART.
	  gpio_enable_module(USART_GPIO_MAP,
	                     sizeof(USART_GPIO_MAP) / sizeof(USART_GPIO_MAP[0]));

	  // Initialize USART in RS232 mode.
	  usart_init_rs232(&AVR32_USART1, &USART_OPTIONS, FOSC0);

	  INTC_register_interrupt(&usart_irq, AVR32_USART1_IRQ, AVR32_INTC_INT2);

	  AVR32_USART1.ier = AVR32_USART_IER_RXRDY_MASK;
}

/** @brief
 * Méthode principale du programme
 * Initialise les interrupts puis boucle à l'infini.
 * Traite les événements de bouton et de lecture/écriture
 * sur le port sériel quand un flag est levé.
 * */
int main(void)
{
	/********** START INIT **********/
	Disable_global_interrupt();
	INTC_init_interrupts();     // Initialise les vecteurs d'interrupt

	tc_init();
	adc_init();
	usart_init();

	Enable_global_interrupt();

	gpio_clr_gpio_pin(LED0_GPIO);
	gpio_set_gpio_pin(LED0_GPIO);
	gpio_clr_gpio_pin(LED1_GPIO);
	gpio_set_gpio_pin(LED1_GPIO);
	gpio_clr_gpio_pin(LED2_GPIO);
	gpio_set_gpio_pin(LED2_GPIO);
	gpio_clr_gpio_pin(LED3_GPIO);
	gpio_set_gpio_pin(LED3_GPIO);
	/*********** END INIT ***********/

	/*********** START LOOP ***********/
	while(1)
	{
		/*********** START PB MANAGEMENT ***********/
		if (gpio_get_pin_value(GPIO_PUSH_BUTTON_0) == RELEASED)
		{
			PB0_PUSHED = PUSHED;
		}
		else
		{
			if(PB0_PUSHED == PUSHED)
			{

				if(tc_read_rc(&AVR32_TC, 0) == NORMAL_SPEED)
				{
					tc_write_rc(&AVR32_TC, 0, OVERDRIVE);
	                tc_configure_interrupts(&AVR32_TC, 0, &TC_INTERRUPT);
	                tc_start(&AVR32_TC, 0);
					LED_FREQUENCY = LED_FREQUENCY << 1;
				}
				else
				{
					tc_write_rc(&AVR32_TC, 0, NORMAL_SPEED);
	                tc_configure_interrupts(&AVR32_TC, 0, &TC_INTERRUPT);
	                tc_start(&AVR32_TC, 0);
					LED_FREQUENCY = LED_FREQUENCY >> 1;
				}
			}
			PB0_PUSHED = RELEASED;
		}
		/*********** END PB MANAGEMENT ***********/

		if(READ_SAMPLE == BUSY)
		{
			READ_SAMPLE = READY;
			adc_start(&AVR32_ADC);
			if(!usart_tx_ready(&AVR32_USART1)) gpio_clr_gpio_pin(LED3_GPIO);
			usart_write_char(&AVR32_USART1, (SAMPLE << 1) | OUTPUT_CHANNEL);
		}
	}
	/*********** END LOOP ***********/
}
